// e_Portals.cpp
// Greg Dillon 4/13/11
// Based on e_Portal1.cc from QE Help file

#include "config.h"
#if BUILD_Portal // compile this app

////////////////////////////////////////////////////////////////////////////////
// Header

#include "qec.h" // engine interface
#include "demoncore.h" // DePaul's core library

#define BOUNDS_NEG -21 // GHD 04/13/11; for negative Portal boundaries
#define BOUNDS_POS 21 // GHD 04/13/11; for positive Portal boundaries

////////////////////////////////////////////////////////////////////////////////
// Portal Class
// JFL 07/29/06
class Portal : public qe 
{
   public:
      chr namePortal[16]; // name of object
      float tLast; // last update time in seconds
      Vec3 xyzPortal; // position
      Vec3 velPortal; // velocity
      float yaBaseRad; // Y angle of base in radians
      float yaTurretRad; // Y angle of turret in radians
      float colorPortal[3]; // GHD 04/13/11; Portal color

      // functions
      Portal(); // constructor
      int update(); // update
      int draw(); // draw
      void zap(); // controlled delete
}; // Portal

////////////////////////////////////////////////////////////////////////////////
// Thing Class
// GHD 04/13/11
class Thing : public qe 
{
   public:
      chr nameThing[16]; // name of object
      float tLast; // last update time in seconds
      Vec3 xyzThing; // position

      // functions
      Thing(); // constructor
      int draw(int i); // draw
      void zap(); // controlled delete
}; // Thing

////////////////////////////////////////////////////////////////////////////////
// Game Struct
// JFL 07/29/06
struct 
{
   // pointers to instances, elements zero if not valid
   Portal *objInstance[10]; 
   Thing *thingInstance[3]; // GHD 04/13/11; added for non-Portal objects
   int idGame; // game id
} Game; // Game

////////////////////////////////////////////////////////////////////////////////
// Geometry for Portal and Turret

// mesh Portal_base
// Created by Vadim Flaks, Fall 2006

// 52 verts
float Portal_base_verts[]=
{
   -1.64396,-0.5,-2.72742, -1.64396,-0.5,2.58847,
   -1.64396,0.5,-2.72742, -1.64396,0.5,2.58847,
   1.64396,0.5,-2.72742, 1.64396,0.5,2.58847,
   1.64396,-0.5,-2.72742, 1.64396,-0.5,2.58847,
   1.64396,-0.275,2.78771, -1.64396,-0.275,2.78771,
   1.64396,0.275,2.78771, -1.64396,0.275,2.78771,
   1.64396,-0.275,-2.92666, -1.64396,-0.275,-2.92666,
   -1.64396,0.275,-2.92666, 1.64396,0.275,-2.92666,
   -1.76531,-0.648395,-3.02, -1.76531,-0.648395,2.88105,
   -1.76531,0.522113,2.88105, -1.76531,0.522113,-3.02,
   -1.76531,-0.38503,3.14965, -1.76531,0.258749,3.14965,
   -1.76531,0.258749,-3.2886, -1.76531,-0.38503,-3.2886,
   1.76531,0.522113,-3.02, 1.76531,0.522113,2.88105,
   1.76531,-0.648395,2.88105, 1.76531,-0.648395,-3.02,
   1.76531,0.258749,3.14965, 1.76531,-0.38503,3.14965,
   1.76531,-0.38503,-3.2886, 1.76531,0.258749,-3.2886,
   -2.24497,-0.648395,-3.02, -2.24497,-0.648395,2.88105,
   -2.24497,0.522113,2.88105, -2.24497,0.522113,-3.02,
   -2.24497,-0.38503,3.14965, -2.24497,0.258749,3.14965,
   -2.24497,0.258749,-3.2886, -2.24497,-0.38503,-3.2886,
   2.24497,0.522113,-3.02, 2.24497,0.522113,2.88105,
   2.24497,-0.648395,2.88105, 2.24497,-0.648395,-3.02,
   2.24497,0.258749,3.14965, 2.24497,-0.38503,3.14965,
   2.24497,-0.38503,-3.2886, 2.24497,0.258749,-3.2886,
   -1.01162,0.924861,-2.03604, -1.01162,0.924861,0.426349,
   1.01162,0.924861,0.426349, 1.01162,0.924861,-2.03604,
}; // Portal_base_verts[]

// 58 faces
int Portal_base_faces[]=
{
   4,32,33,34,35, 4,48,49,50,51, 4,40,41,42,43, 4,6,7,1,0, 4,9,8,10,11,
   4,12,13,14,15, 4,1,7,8,9, 4,42,41,44,45, 4,5,3,11,10, 4,34,33,36,37,
   4,6,0,13,12, 4,32,35,38,39, 4,2,4,15,14, 4,40,43,46,47, 4,0,1,17,16,
   4,3,2,19,18, 3,20,17,9, 3,1,9,17, 4,9,11,21,20, 3,21,11,18, 3,11,3,18,
   3,22,19,14, 3,2,14,19, 4,14,13,23,22, 3,23,13,16, 3,13,0,16, 4,4,5,25,24,
   4,7,6,27,26, 3,28,25,10, 3,5,10,25, 4,10,8,29,28, 3,29,8,26, 3,8,7,26,
   3,30,27,12, 3,6,12,27, 4,12,15,31,30, 3,31,15,24, 3,15,4,24, 4,16,17,33,32,
   4,18,19,35,34, 4,17,20,36,33, 4,20,21,37,36, 4,21,18,34,37, 4,19,22,38,35,
   4,22,23,39,38, 4,23,16,32,39, 4,24,25,41,40, 4,26,27,43,42, 4,25,28,44,41,
   4,28,29,45,44, 4,29,26,42,45, 4,27,30,46,43, 4,30,31,47,46, 4,31,24,40,47,
   4,2,3,49,48, 4,3,5,50,49, 4,5,4,51,50, 4,4,2,48,51,
}; // Portal_base_faces[]

// Portal_base geometry
qeGeo1 Portal_base_geo = 
{
   52, // numv
   58, // numf
   Portal_base_verts, // verts
   Portal_base_faces, // faces
}; // Portal_base_geo

// mesh Portal_turret
// Created by Vadim Flaks

// 26 verts
float Portal_turret_verts[]=
{
   -0.903872,-0.288892,-0.372658, -0.903873,-0.288892,0.372658,
   -0.38165,-0.288891,0.87866, -0.903872,0.288891,-0.372658,
   -0.903873,0.288891,0.372658, -0.38165,0.288892,0.87866,
   -0.38165,0.288891,-0.899676, -9.32094e-009,0.513876,1.21426e-008,
   0.38165,0.288891,0.87866, 0.38165,0.288891,-0.899676,
   0.903872,0.288891,-0.372658, 0.903872,0.288891,0.372658,
   0.38165,-0.288892,-0.899676, 0.903872,-0.288892,-0.372658,
   0.903872,-0.288892,0.372658, -0.38165,-0.288892,-0.899676,
   -9.32094e-009,-0.288892,1.21426e-008, 0.38165,-0.288892,0.87866,
   0.169766,-0.202795,1, -0.169766,-0.202795,1, 0.169766,0.117233,1,
   -0.169766,0.117233,1, 0.169766,-0.202795,3.41435,
   -0.169767,-0.202795,3.41435, 0.169766,0.117233,3.41435,
   -0.169767,0.117233,3.41435,
}; // Portal_turret_verts[]

// 28 faces
int Portal_turret_faces[]=
{
   4,0,1,4,3, 4,1,2,5,4, 3,4,7,3, 3,3,7,6, 3,4,5,7, 3,5,8,7,
   3,6,7,9, 3,7,10,9, 3,10,7,11, 3,7,8,11, 4,9,10,13,12, 4,10,11,14,13,
   4,12,13,16,15, 4,13,14,17,16, 4,15,16,1,0, 4,16,17,2,1, 4,17,14,11,8,
   4,23,22,24,25, 4,12,15,6,9, 4,15,0,3,6, 4,2,17,18,19, 4,17,8,20,18,
   4,8,5,21,20, 4,5,2,19,21, 4,19,18,22,23, 4,18,20,24,22, 4,20,21,25,24,
   4,21,19,23,25,
}; // Portal_turret_faces[]

// Portal_turret geometry
qeGeo1 Portal_turret_geo = 
{
   26, // numv
   28, // numf
   Portal_turret_verts, // verts
   Portal_turret_faces, // faces
}; // Portal_turret_geo

////////////////////////////////////////////////////////////////////////////////
// Functions

// JFL 09/17/07; code for Portal
Portal::Portal()
{
   szfmt(this->namePortal,
         sizeof(this->namePortal),
         "Portal%d",
         ++Game.idGame); // unique name
   this->tLast=qeTimeFrame(); // time of creation
} // Portal::Portal()

// JFL 07/23/09; qe object delete
void Portal::zap()
{
   int i;

   // remove reference from array
   for(i=0;i<NUM(Game.objInstance);i++)
   {
      if(Game.objInstance[i]==this)
         Game.objInstance[i]=0;
   } // for

   delete this; // free memory
} // Portal::zap()

// JFL 07/29/06; update Portal object
int Portal::update()
{
   float t; // delta time

   // get elapsed time since last update
   t = this->tLast; // last update time
   this->tLast = qeTimeFrame(); // current frame time
   t = this->tLast - t; // elapsed time

   // integrate: position, velocity, acceleration formula
   //  pos += vel*t
   Vec3 tempXyz = this->xyzPortal + this->velPortal * t;

   // GHD 04/13/11; bounds check for Portal
   if(tempXyz[0] > BOUNDS_POS 
      || tempXyz[0] < BOUNDS_NEG 
      || tempXyz[2] > BOUNDS_POS 
      || tempXyz[2] < BOUNDS_NEG)
   {
      float xx, zz;
      // setup rand XZ vel
      #define START_VEL 3 // vel in units (feet) per second
      xx = (qeRand01() * START_VEL) - (START_VEL * 0.5);
      zz = (qeRand01() * START_VEL) - (START_VEL * 0.5);
      this->velPortal.set(xx,0,zz); // set Portal velocity
   }
   else
   {
      this->xyzPortal += this->velPortal * t; // position change from velocity
   } // else

   // orient the Portal in the direction it is moving
   this->yaBaseRad = MathATan2f(this->velPortal.x,this->velPortal.z);
   this->yaTurretRad = this->yaTurretRad + .01;

   return 0;
} // Portal::update()

// JFL 09/20/06; draws Portal object
int Portal::draw(void)
{
   float deg, degTur; // degrees for rotation

   // GHD 04/13/11; set color based on Portal color property
   glColor3f(this->colorPortal[0],this->colorPortal[1],this->colorPortal[2]); 

   glPushMatrix(); // save matrix

   // draw the base
   glTranslatef(this->xyzPortal.x,this->xyzPortal.y,this->xyzPortal.z); // position
   deg = (this->yaBaseRad * 180.0) / PI; // radians -> degrees
   glRotatef(deg,0,1,0); // rotate the base
   qeGeo1 *geo = &Portal_base_geo; // pointer to Portal base geometry
   qeDrawFaces(geo->verts,geo->faces,geo->numf);
 
   // GHD 04/13/11; draw the turret here
   glTranslatef(0,1,0); // moves turret on top of Portal base
   degTur = (this->yaTurretRad*180.0) / PI; // radians -> degrees
   glRotatef(degTur,0,1,0); // rotate the base
   qeGeo1 *geoTur = &Portal_turret_geo; // pointer to Portal turret geometry
   qeDrawFaces(geoTur->verts,geoTur->faces,geoTur->numf);

   glPopMatrix(); // restore matrix

   return 0;
} // Portal::draw()

// GHD 04/13/11; code for Thing
Thing::Thing()
{
   szfmt(this->nameThing,
         sizeof(this->nameThing),
         "thing%d",++Game.idGame); // unique name
   this->tLast=qeTimeFrame(); // time of creation
} // Thing::Thing()

// draws different Thing object based on index 
int Thing::draw(int i)
{

   glPolygonMode(GL_FRONT,GL_LINE); // draw wireframe polygons
   glCullFace(GL_BACK); // don't draw back faces
   glEnable(GL_CULL_FACE); // don't draw back faces

   switch(i) // i determines shape to draw
   {
      case 0: // cube
      {
         // draw Thing
         glColor3f(1,0,0); // set color red
         // draw the thing
         glTranslatef(-10, 1, 0); // position
         glBegin(GL_QUADS);

         // face 1
         glVertex3f(-1,1,1);
         glVertex3f(-1,-1,1);
         glVertex3f(1,-1,1);
         glVertex3f(1,1,1);
         // face 2
         glVertex3f(-1,1,-1);
         glVertex3f(1,1,-1);
         glVertex3f(1,-1,-1);
         glVertex3f(-1,-1,-1);
         // face 3
         glVertex3f(1,1,1);
         glVertex3f(1,-1,1);
         glVertex3f(1,-1,-1);
         glVertex3f(1,1,-1);
         // face 4
         glVertex3f(-1,1,1);
         glVertex3f(1,1,1);
         glVertex3f(1,1,-1);
         glVertex3f(-1,1,-1);
         // face 5
         glVertex3f(1,-1,1);
         glVertex3f(-1,-1,1);
         glVertex3f(-1,-1,-1);
         glVertex3f(1,-1,-1);
         // face 6
         glVertex3f(-1,1,1);
         glVertex3f(-1,1,-1);
         glVertex3f(-1,-1,-1);
         glVertex3f(-1,-1,1);
         glEnd();
      } // case 0
      break;
      case 1: // pyramid
      {
         glColor3f(1,0,1); // set color purple
         glTranslatef(20, 0, 0); // position
         glBegin(GL_TRIANGLES); // for sides

         // face 1
         glVertex3f(0,1,0);
         glVertex3f(-1,-1,1);
         glVertex3f(1,-1,1);
         // face 2
         glVertex3f(0,1,0);
         glVertex3f(-1,-1,-1);
         glVertex3f(-1,-1,1);
         // face 3
         glVertex3f(0,1,0);
         glVertex3f(1,-1,-1);
         glVertex3f(-1,-1,-1);
         // face 4
         glVertex3f(0,1,0);
         glVertex3f(1,-1,1);
         glVertex3f(1,-1,-1);
         glEnd();
         // face 5
         glBegin(GL_QUADS); // for base
         glVertex3f(1,-1,1);
         glVertex3f(-1,-1,1);
         glVertex3f(-1,-1,-1);
         glVertex3f(1,-1,-1);
         glEnd();
      } // case 1
      break;
      case 2: // diamond
      {
         glColor3f(0,1,0);
         glTranslatef(-10, 2, -10); // position
         glBegin(GL_TRIANGLES); // for sides

         // face 1
         glVertex3f(0,1,0);
         glVertex3f(-1,-1,1);
         glVertex3f(1,-1,1);
         // face 2
         glVertex3f(0,1,0);
         glVertex3f(-1,-1,-1);
         glVertex3f(-1,-1,1);
         // face 3
         glVertex3f(0,1,0);
         glVertex3f(1,-1,-1);
         glVertex3f(-1,-1,-1);
         // face 4
         glVertex3f(0,1,0);
         glVertex3f(1,-1,1);
         glVertex3f(1,-1,-1);
         // face 5
         glVertex3f(0,-3,0);
         glVertex3f(1,-1,1);
         glVertex3f(-1,-1,1);
         // face 6
         glVertex3f(0,-3,0);
         glVertex3f(-1,-1,1);
         glVertex3f(-1,-1,-1);
         // face 7
         glVertex3f(0,-3,0);
         glVertex3f(-1,-1,-1);
         glVertex3f(1,-1,-1);
         // face 8
         glVertex3f(0,-3,0);
         glVertex3f(1,-1,-1);
         glVertex3f(1,-1,1);
         glEnd();
      } // case 2
      break;
   } // switch(i)

   return 0;
} // Thing::draw()

void Thing::zap()
{
   int i;

   // remove reference from array
   for(i=0;i<NUM(Game.thingInstance);i++)
   {
      if(Game.thingInstance[i]==this)
         Game.thingInstance[i]=0;
   } // for

   delete this; // free memory
} // Thing::zap()

////////////////////////////////////////////////////////////////////////////////
// Game Functions
// JFL 07/22/09

// initial frame setup drawing axes and grid
int gameStartFrame()
{
   qefnDrawAxes(1); // draw origin's axes
   qefnDrawGrid(50,1); // draw 50x50 grid with 1x1 unit squares

   glPolygonMode(GL_FRONT,GL_LINE); // draw wireframe polygons
   glCullFace(GL_BACK); // don't draw back faces
   glEnable(GL_CULL_FACE); // don't draw back faces

   return 0;
} // gameStartFrame()

// main loop added to qe
int gameMainLoop()
{
   int i;
   Portal *obj;
   Thing *thng;

   gameStartFrame(); // setup defaults and draw grid

   // run through all instances of objects, update
   for(i=0;i<NUM(Game.objInstance);i++)
   {
      if(!(obj=Game.objInstance[i]))
         continue; // nothing in this entry, skip
      if(obj->update()>=0) // objects update themselves
         continue; // do not delete, skip

      // delete object & clear reference
      obj->zap(); // request obj delete itself
      Game.objInstance[i]=0; // clear instance reference
   } // for

   // run through all instances of objects, draw
   for(i=0;i<NUM(Game.objInstance);i++)
   {
      if(!(obj=Game.objInstance[i]))
         continue; // element empty, skip
      obj->draw(); // objects draw themselves
   } // for

   // run through all instances of things, draw
   for(i=0;i<NUM(Game.thingInstance);i++)
   {
      if(!(thng=Game.thingInstance[i]))
         continue; // element empty, skip
      thng->draw(i); // things draw themselves
   } // for

   return 0;
} // gameMainLoop()

// sets up the Portal and Thing objects
int gameSetup()
{
   int i;
   Portal *obj;
   Thing *thng;
   float xx,zz; // used for Portal velocity
 
   // run through and create elements
   for(i=0;i<NUM(Game.objInstance);i++)
   {
      // create Portal
      if(!(obj = new Portal()))
      {
         BRK(); // error
         break; // exit loop
      }

      Game.objInstance[i]=obj; // set reference

      // setup rand XZ vel
      #define START_VEL 3 // vel in units (feet) per second
      xx=(qeRand01()*START_VEL)-(START_VEL*0.5); // move along x
      zz=(qeRand01()*START_VEL)-(START_VEL*0.5); // move along z
      obj->velPortal.set(xx,0,zz); // set new velocity

      // GHD 04/13/11; Set Portal color
      if(obj == Game.objInstance[0] || obj == Game.objInstance[1])
      {
         // first 2 Portals are blue
         obj->colorPortal[0] = 0; 
         obj->colorPortal[1] = 0;
         obj->colorPortal[2] = 1;
      }
      else
      {
         // the rest are grey
         obj->colorPortal[0] = .5;
         obj->colorPortal[1] = .5;
         obj->colorPortal[2] = .5;
      } // else
   } // for

   for(i=0;i<NUM(Game.thingInstance);i++)
   {
      // create thing
      if(!(thng = new Thing()))
      {
         BRK(); // error
         break; // exit loop
      }

      Game.thingInstance[i]=thng; // set reference
   } // for

   return 0;
} // gameSetup()

// JFL 07/22/09
int gameInit()
{
   MEMZ(Game); // initialize game globals to zero
   return 0; // return negative error code only w/catastrophic failure
} // gameInit()

// JFL 07/22/09
void gameFinal()
{
   int i;
   Portal *obj;
   Thing *thng;

   // run through all the elements in the array
   for(i=0;i<NUM(Game.objInstance);i++)
   {
      if(!(obj=Game.objInstance[i]))
         continue; // element empty, skip
      obj->zap(); // delete
      Game.objInstance[i]=0; // clear reference
   } // for

   // GHD 04/13/11; delete Thing objects
   for(i=0;i<NUM(Game.thingInstance);i++)
   {
      if(!(thng=Game.thingInstance[i]))
         continue; // element empty, skip
      thng->zap(); // delete
      Game.thingInstance[i]=0; // clear reference
   } // for

} // gameFinal()

////////////////////////////////////////////////////////////////////////////////
// Main

int qeMain(int argc,chr *argv[])
{
   int fail=0; // failure

   // INIT

   qePrintf("%s / %s / %s\n",__FILE__,glGetString(GL_VERSION),qeVersion());

   if(gameInit()<0) // initialize game
   {
      fail=1;
   } // if

   if(fail) // check for catastrophic failure in init
   {
      goto BAIL; // end
   } // if

   // BODY

   // add a function object to engine
   if(!qeObjAddFnc(gameMainLoop))
   {
      BRK(); // error
      goto BAIL; // end
   }

   gameSetup(); // setup game objects
 
   qeForever(); // turn over control to the engine until the program exits

BAIL:

   // FINAL

   gameFinal(); // cleanup and free game objects

   if(qefnCheckForCleanExit()<0)
   {
      BRK(); // check qelog.txt for problem
   } // if

   return 0;
} // qeMain()

#endif // compile this app
// EOF